/**
* @program: src
*
* @description:
*
* @author: Mr.chen
*
* @create: 2022-03-28 17:45
**/
package etcdregister

import (
	"fmt"
	"errors"
	"log"
	"math/rand"
	"strconv"
	"strings"
	"sync"
	"ydd-im/service/api/tools/balance"
)

type NodeInfo struct {
	Addr     string
	Name     string
	UniqueId string
}

type NodesManager struct {
	sync.RWMutex
	// <name,<id,node>>
	nodes map[string]map[string]*NodeInfo
}

func NewNodeManager() (m *NodesManager){
	return &NodesManager{
		nodes: map[string]map[string]*NodeInfo{},
	}
}

func (n *NodesManager) AddNode(node *NodeInfo) {
	if node == nil {
		return
	}
	n.Lock()
	defer n.Unlock()
	if _, exist := n.nodes[node.Name]; !exist {
		n.nodes[node.Name] = map[string]*NodeInfo{}
	}
	n.nodes[node.Name][node.UniqueId] = node
}

func (n *NodesManager) DelNode(id string) {
	sli := strings.Split(id,"/")
	name := sli[len(sli)-3]
	n.Lock()
	defer n.Unlock()
	log.Printf("[NodesManager] delete Nodes:%v",n.nodes)
	log.Printf("[NodesManager] delete Name:%s",name)
	log.Printf("[NodesManager] delete id:%s",id)
	if _, exist := n.nodes[name]; exist {
		delete(n.nodes[name], id)
	}
}

func (n *NodesManager) Pick(name string) *NodeInfo {
	n.RLock()
	defer n.RUnlock()
	if nodes, exist := n.nodes[name]; !exist {
		return nil
	} else {
		// 纯随机取节点
		idx := rand.Intn(len(nodes))
		for _, v := range nodes {
			if idx == 0 {
				return v
			}
			idx--
		}
	}
	return nil
}

func (n *NodesManager) Dump () {
	fmt.Println("[NodesManager] Nodes num",n.nodes)
	for k, v := range n.nodes {
		for kk, vv := range v {
			log.Printf("[NodesManager] Name:%s Id:%s Node:%+v", k, kk, vv)
		}
	}

}
// 根据服务名获取所有的节点
func (n *NodesManager) GetNodes(name string) (addr []string) {
	n.RLock()
	defer n.RUnlock()
	addr = make([]string,0)
	if nodes, exist := n.nodes[name]; !exist {
		return addr
	} else {
		for k,_:= range  nodes  {
			addr = append(addr,nodes[k].Addr)
		}
	}
	return addr
}


/**
 * 负载均衡算法
 * @access  public
 * @param   string     name  节点的名称
 * @param   string     balanceName  名称，roundrobin(轮询)，random(随机)
 * @return  string addr 地址
 * @return  error err
 */
func (n *NodesManager) GetBalancerNodes(name,balanceName string) (addr string,err error) {
	n.RLock()
	defer n.RUnlock()
	addrs := n.GetNodes(name)
	if len(addrs) == 0 {
		return "",errors.New("Service unavailable, please try again")
	}
	insts := make([]*balance.Instance,0)
	for k,_:= range addrs  {
		sli := strings.Split(addrs[k],":")
		if len(sli) !=2 {
			continue
		}
		port,err:= strconv.Atoi(sli[1])
		if err != nil {
			continue
		}
		instance := balance.NewInstance(sli[0],port)
		insts = append(insts,instance)
	}
	if len(insts) == 0 {
		return "",errors.New("Service unavailable, please try again")
	}
	inst, err := balance.DoBalance(balanceName, insts)
	if err != nil {
		return "",errors.New("The load balancing service is unavailable, please try again")
	}
	addr = fmt.Sprintf("%s:%d", inst.GetHost(), inst.GetPort())
	return addr,nil
}